/**
 * 
 */
package com.vnetoo.osfilesvr;

import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.net.Socket;
import java.sql.Connection;
import java.util.List;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.digester.Digester;
import org.apache.commons.io.FileSystemUtils;
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;


/**
 * @author chenh
 *
 */
public class ServerThread implements Runnable{
	
	public static final Logger log=Logger.getLogger(ServerThread.class);
		
	private Socket clientSocket;
	private JdbcTemplate jt;
	private String path;	
	private FileInfo fi;
	private String fileName;//文件名
		
	public ServerThread(Socket clientSocket){		
		this.clientSocket = clientSocket;	
		
	}
	
	public void run(){
		
		DataOutputStream outData;
		DataInputStream inData;
		BufferedOutputStream bos=null;
		try 
		{
			outData = new DataOutputStream(clientSocket.getOutputStream());
			inData = new DataInputStream(clientSocket.getInputStream());
								
			/*fileName=readString(inData);//文件名
			log.info(fileName);
			long fileSize=inData.readLong();//文件长度
*/
			fi=readFileInfo(inData);
			jt=getJdbcTemplate();
			if(fi.getRemoveIDs()!=null&&!fi.getRemoveIDs().equals("")){
				//删除操作
				//removeCourseware(fi.getRemoveIDs());
				return;
			}
			if(fi.getPathid()!= null && !fi.getPathid().equals("")){
				//操作存储路径
				//dealStorePath2(Long.parseLong(fi.getPathid()),fi.getVpath(),fi.getRpath());
				return;
			}
			int index=fi.getName().lastIndexOf(".");
			String extName=fi.getName().substring(index);
			//fi.setNewName(""+System.currentTimeMillis()+extName);
			fi.setNewName(fi.getName());
			
			fi.setName(fi.getName().substring(0,index));
			long fileSize=fi.getSize();
			
			//jt=getJdbcTemplate();
			Boolean exist=checkCourseware();
			if(exist!=null&&exist){
				//已经上传过了
				log.info("file exist!");
				outData.writeInt(Constant.RET_MSG_FILE_EXIST);
				outData.flush();
				return;
			}
						
			Long courseId=fi.getCourseId();
			log.info("get course: "+courseId);
			if(courseId==0)
				courseId=findCourseId(fi.getMcuId(),fi.getConfId());
			log.info("find course: "+courseId);
			path=findPathByCourseId(courseId);
			log.info("find path: "+path);
			
			fileName=fi.getNewName();
			if(log.isInfoEnabled()){								
				log.info("orig  file size: "+fileSize);
			}
			if(path==null){
				log.error("path not exist");
				outData.writeInt(Constant.RET_MSG_PATH_NOT_EXIST);
				return;
			}
			
			/*double freeSpace=FileSystemUtils.freeSpaceKb(path)*1024;
			if(freeSpace<fileSize){
				//空间不足
				log.info("not enough space!");
				outData.writeInt(Constant.RET_MSG_SPACE_NOT_ENOUGH);
				outData.flush();
				return;
			}*/
			
			if(fileName==null){
				log.error("path not exist");
				outData.writeInt(Constant.RET_MSG_PATH_NOT_EXIST);
				return;
			}
			
			Long recoredId=createFileUploadRecord(exist==null);
			
			File f=new File(new File(path),fileName);
			long fileS=0;
			if(f.exists()){
				if(exist==null){
					//同名不同文件
				}
				fileS=f.length();
				log.info("file: "+f.getAbsolutePath()+" is exist! file size: "+f.length());
				if(f.length()>=fileSize){
					log.info("file: "+f.getAbsolutePath()+" is exist! over write?");
					outData.writeInt(Constant.RET_MSG_FILE_EXIST);
					outData.flush();
					return;
				}else{
					log.info("file: "+f.getAbsolutePath()+" is exist! go on?");
					outData.writeInt(Constant.RET_MSG_GO_ON);
					outData.writeLong(f.length());//文件开始点
					outData.flush();
				}
			}else{
				if(!f.getParentFile().exists()){
					f.getParentFile().mkdirs();
				}
				f.createNewFile();
				outData.writeInt(Constant.RET_MSG_OK);
				outData.flush();
			}
			log.info("recv file...");
			bos=new BufferedOutputStream(new FileOutputStream(f,true));
			long resvSize=0;
			byte b[]=new byte[1024*16];
			int len=inData.read(b);
			if(FileServer.FILE_LOG)
				log.info("file first recv: "+len);
			//while(len>0){
			while(len!=-1){
				bos.write(b, 0, len);
				bos.flush();
				resvSize+=len;
				if(FileServer.FILE_LOG)
					log.info("recv: "+len+" total size: "+resvSize);
				if((resvSize+fileS)<fileSize){
					try{
						len=inData.read(b);						
					}catch(Exception ex){
						ex.printStackTrace();
						log.error(ex.getMessage());
						len=-1;
					}
				}
				else
					break;
			}
			if(log.isInfoEnabled())
				log.info("resv file size: "+resvSize);
			
			if((resvSize+fileS)==fileSize){	
				if(finish(courseId,recoredId))
					outData.writeInt(Constant.RET_MSG_OK);
				else
					outData.writeInt(Constant.RET_MSG_DB_ERROR);
			}
			else
				outData.writeInt(Constant.RET_MSG_FILE_TERM);
			outData.flush();
			bos.close();
			
			
		}
		catch (Exception e){			
			//log.error(e.getMessage());
			e.printStackTrace();
		} 
		finally{
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {}
			
			if(log.isInfoEnabled())
				log.info("Remove connection: " + clientSocket+"\n");
			try {	
				if(bos!=null)
					bos.close();
				clientSocket.close();				
			} catch (Exception ex) {				
			}
		}
	}	
	class PathItem{
		int id;
		String rpath;
		String vpath;
	}
	/**
	 * 处理存储路径
	 * @param storePathId
	 */
	private void dealStorePath(long storePathId)throws Exception{
		String sql="select id id,HP_PATH rpath,VIRSUAL_PATH vpath from STORE_PATH where enabled_flag=1 and id="+storePathId;
		log.info(sql);
		List list=jt.query(sql, new RowMapper(){
			public Object mapRow(java.sql.ResultSet rs, int i) throws java.sql.SQLException{
				PathItem pathItem=new PathItem();
				pathItem.id=rs.getInt("id");
				pathItem.rpath=rs.getString("rpath");
				pathItem.vpath=rs.getString("vpath");
				return pathItem;
			}
		});
		String cmd="./pathmgr.sh "+RunServer.apachefile+" "+storePathId;
		
		if(list==null||list.isEmpty()){
			//删除操作
			log.info(cmd);
			Runtime.getRuntime().exec(cmd).getInputStream().read();
			return;
		}
		PathItem pathItem=(PathItem)list.get(0);
		if(pathItem.rpath==null||pathItem.rpath.equals("") ||
				pathItem.vpath==null||pathItem.vpath.equals(""))
			return;
		
		//创建目录
		File pathFile=new File(pathItem.rpath);
		log.info("mkdir: "+pathFile.getPath());
		if(!pathFile.exists())
			pathFile.mkdirs();
		//添加或删除操作
		cmd+=" "+pathItem.vpath+" "+pathItem.rpath+"";
		log.info(cmd);
		Runtime.getRuntime().exec(cmd).getInputStream().read();
	}
	
	/**
	 * 处理存储路径
	 * @param storePathId
	 */
	private void dealStorePath2(long storePathId,String vpath,String rpath)throws Exception{
		
		String cmd="./pathmgr.sh "+RunServer.apachefile+" "+storePathId;
		
		//删除操作
		log.info(cmd);
		Runtime.getRuntime().exec(cmd).getInputStream().read();
				
		if(vpath==null||vpath.equals("") ||
				rpath==null||rpath.equals(""))
			return;
		
		//创建目录
		File pathFile=new File(rpath);
		log.info("mkdir: "+rpath);
		if(!pathFile.exists())
			pathFile.mkdirs();
		
		//添加或删除操作
		cmd+=" "+vpath+" "+rpath+"";
		log.info(cmd);
		Runtime.getRuntime().exec(cmd).getInputStream().read();
	}
		
	/**
	 * 读取文件信息对象
	 * @param in
	 * @param actionPath
	 * @return
	 * @throws Exception
	 */
	private FileInfo readFileInfo(DataInputStream in)throws Exception{		
		int len=in.readInt();
		log.info("xml length: "+len);
		byte[] bt=new byte[len];		
		int totalReaded=0;
		int size=in.read(bt);
		totalReaded+=size;
		while(len-totalReaded>0){
			size=in.read(bt, totalReaded, len-totalReaded);
			totalReaded+=size;
		}				
		log.info(new String(bt,"UTF-8"));
		ByteArrayInputStream bin=new ByteArrayInputStream(bt);
		Digester digester = new Digester();		
		digester.addObjectCreate("file",FileInfo.class);
		digester.addSetProperties("file");		
		return (FileInfo)digester.parse(bin);	
	}
	
	/**
	 * @return JdbcTemplate
	 */
	private JdbcTemplate getJdbcTemplate(){
		ApplicationContext ac= new FileSystemXmlApplicationContext(
				"datasourse.xml");
		BasicDataSource ds=(BasicDataSource)ac.getBean("dataSource");
				
		return new JdbcTemplate(ds);
	}
	
	/**
	 * 根据设备ＩＤ，会议室Id找到课程Id
	 * @param confId
	 * @return
	 */
	private Long findCourseId(long mcuId,long confId){
		//贵
		//String sql="SELECT A.ID FROM EDU_CURRICULA A,REF_COURSE_CONF B WHERE A.ID=B.COURSE_ID AND B.CONFERENCE_ID="+confId;
		
		/*String sql = "select c.id from edu_curricula c," +
				"local_remote_ref r," +
				"remote_course rc," +
				"device d " +
				"where rc.conf_id=" + confId +
				" and d.id=" + mcuId +
				" and d.school=rc.school " +
				" and rc.id=r.remote_course_id " +
				" and c.id=r.local_course_id";*/
		
		String sql = "select curr_id from PLAN t where conf_id="+confId;
		
		log.info(sql);
		try{
			return jt.queryForLong(sql);
		}
		catch(Exception e){
			log.error(e.getMessage());
			return new Long(0);
		}
	}
	
	/**
	 * 根据课程ID找到物理路径
	 * @param courseId
	 * @return
	 */
	private String findPathByCourseId(Long courseId){
		String sql="SELECT A.HP_PATH path FROM STORE_PATH A,REF_CURRICULA_PATH B WHERE B.STORE_PATH_ID=A.ID AND B.CURRICULA_ID="+courseId+" and a.enabled_flag=1 order by A.id ";
		log.info(sql);
		List list=jt.query(sql, new RowMapper(){
			public Object mapRow(java.sql.ResultSet rs, int i) throws java.sql.SQLException{				
				return rs.getString("path");
			}
		});
		if(list==null||list.isEmpty())
			return null;
		return (String)list.get(0);
		/*try{
			return (String)jt.queryForObject(sql, String.class);
		}catch(Exception e){
			log.error(e.getMessage());
			return null;
		}*/
	}
	
	/**
	 * 根据课程ID找到物理路径
	 * @param courseId
	 * @return
	 */
	private Long findPathIdByCourseId(Long courseId){
		String sql="SELECT A.ID id FROM STORE_PATH A,REF_CURRICULA_PATH B WHERE B.STORE_PATH_ID=A.ID AND B.CURRICULA_ID="+courseId+" and a.enabled_flag=1 order by A.id";
		log.info(sql);
		List list=jt.query(sql, new RowMapper(){
			public Object mapRow(java.sql.ResultSet rs, int i) throws java.sql.SQLException{				
				return rs.getLong("id");
			}
		});
		if(list==null||list.isEmpty())
			return null;
		return (Long)list.get(0);
	}
	
	/**
	 * 检查是否已经存在课件
	 * @return
	 */
	private Boolean checkCourseware(){
		String sql="select status from FILE_UPLOAD_RECORD where md5='"+fi.getMd5()+"' and enabled_flag=1 and rownum=1 order by id desc";
		log.info(sql);
		int status=0;
		try{
			status=jt.queryForInt(sql);
		}catch(Exception e){
			//log.error(e.getMessage());
			return null;
		}
		sql="select new_file_name from FILE_UPLOAD_RECORD where md5='"+fi.getMd5()+"' and enabled_flag=1 and rownum=1 order by id desc";
		log.info(sql);
		fi.setNewName((String)jt.queryForObject(sql, String.class));
		return new Boolean(status==1);
	}
	
	/**
	 * 创建文件上传记录,状态未完成
	 * @return
	 */
	private Long createFileUploadRecord(boolean create){
		Long id=new Long(0);
		if(create){
			id=jt.queryForLong("select SEQ_FILE_UPLOAD_RECORD.nextval from dual");
			String sql="Insert into VNETOO.FILE_UPLOAD_RECORD "+
			   "(ID, MD5, NEW_FILE_NAME, FILE_SIZE, STATUS, REAL_FILE_NAME,  " +
			   "LAST_UPDATE_DATE, LAST_UPDATED_BY, CREATION_DATE,CREATED_BY, ENABLED_FLAG) Values "+
			   "("+id+", '"+fi.getMd5()+"', '"+fi.getNewName()+"', "+fi.getSize()+", 0," +
			   " '"+fi.getNewName()+"', SYSDATE, "+fi.getUserId()+", SYSDATE,"+fi.getUserId()+", 1)";
			log.info(sql);
			jt.execute(sql);
		}
		else{
			String sql="select ID from FILE_UPLOAD_RECORD where md5='"+fi.getMd5()+"' and enabled_flag=1 and rownum=1 order by id desc";
			log.info(sql);
			id=jt.queryForLong(sql);
		}
		log.info("create or get recored id: "+id);
		return id;
	}
	
	/**
	 * 更新文件上传记录,状态完成
	 * @param recoredId
	 */
	private void updateFileUploadRecored(Long recoredId,Long cwid){
		String sql="UPDATE VNETOO.FILE_UPLOAD_RECORD SET STATUS=1,COURSEWARE_ID="+cwid+",LAST_UPDATE_DATE=SYSDATE WHERE ID="+recoredId;
		log.info(sql);
		jt.execute(sql);
	}
	
	/**
	 * 创建课件
	 * @return
	 */
	private Long createCourseware(){
		Long id=jt.queryForLong("select SEQ_COURSE_WARE.nextval from dual");
		String sql="Insert into VNETOO.COURSE_WARE "+
			"(ID, NAME, FILE_NAME, FILE_SIZE, TYPE, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME,DESCN,ENABLED,DISABLED)Values "+
			"("+id+", '"+fi.getName()+"', '"+fi.getNewName()+"', "+fi.getSize()+", 1, " +
			fi.getUserId()+", SYSDATE, "+fi.getUserId()+", SYSDATE, '"+fi.getDescription()+"',1,51)";
		log.info(sql);
		jt.execute(sql);	
		return id;
	}
	
	/**
	 * 创建课件课程关系
	 * @return
	 */
	private void createCoursewareCourseRef(Long courseId,Long cwid){
		String sql="Insert into VNETOO.REF_CURR_CWARE(CURR_ID, CWARE_ID)Values("+courseId+", "+cwid+")";
		log.info(sql);
		jt.execute(sql);	
	}
	
	/**
	 * 创建课件课程关系
	 * @return
	 */
	private void createAccessInfo(Long cwid,Long pathId){
		String sql="Insert into VNETOO.ACCESS_INFO(COURSE_ID, STORE_PATH_ID) Values("+cwid+", "+pathId+")";
		log.info(sql);
		jt.execute(sql);	
	}
	
	/**
	 * 上传完成，写数据库
	 */
	private boolean finish(Long courseId,Long recoredId)throws Exception{
		log.info("onfinish()");	
		//if(true)return true;
		Connection con=jt.getDataSource().getConnection();
		con.setAutoCommit(false);
		try{
			Long cwid=createCourseware();
			createCoursewareCourseRef(courseId,cwid);
			createAccessInfo(cwid,findPathIdByCourseId(courseId));
			updateFileUploadRecored(recoredId,cwid);
		}catch(Exception e){
			log.error(e.getMessage());
			con.rollback();
			con.setAutoCommit(true);
			return false;
		}
		con.commit();
		con.setAutoCommit(true);
		return true;
	}
	
	/**
	 * 物理删除课件
	 * @param coursewareId 课件ID字符串
	 */
	private void removeCourseware(String coursewareIDs){
		
		log.info("remove file...");
		String[] ids=coursewareIDs.split(",");
		for (String coursewareId : ids) {
			String path=findCoursewarePath(coursewareId);
			if(path==null)
				continue;
			File file=new File(path);
			if(file.exists()){
				file.delete();
				String sql="UPDATE VNETOO.FILE_UPLOAD_RECORD SET STATUS=0,ENABLED_FLAG=0,LAST_UPDATE_DATE=SYSDATE WHERE COURSEWARE_ID="+coursewareId;
				log.info(sql);
				jt.execute(sql);
				log.info("remove successfully");
			}else
				log.info("file not exist");
		}
	}
	
	/**
	 * 根据课件ID找到物理路径
	 * @param coursewareId 课件ID
	 * @return
	 */
	private String findCoursewarePath(String coursewareId){
		String sql="select sp.hp_path path from ACCESS_INFO ai ,store_path sp where ai.course_id="+coursewareId+" and sp.id=ai.store_path_id order by sp.id";	
		log.info(sql);
		List list=jt.query(sql, new RowMapper(){
			public Object mapRow(java.sql.ResultSet rs, int i) throws java.sql.SQLException{				
				return rs.getString("path");
			}
		});
		if(list==null||list.isEmpty()){
			log.info("find path null");
			return null;
		}
		String path=(String)list.get(0);		
			
		sql="select file_name from course_ware where id="+coursewareId;	
		log.info(sql);
		String fileName;
		try{
			fileName=(String)jt.queryForObject(sql, String.class);
		}catch(Exception e){
			log.info("find fileName null");
			return null;
		}		
		if(path.endsWith("\\")||path.endsWith("/")){
			path+=fileName;
		}
		else{
			path+=File.separator+fileName;
		}
		log.info("find file path: "+path);
		return path;
	}
}





